package com.pccw.hessian.support.client;

import java.lang.reflect.Method;
import java.net.URL;
import java.util.Arrays;

import com.caucho.hessian.client.HessianProxyFactory;
import com.caucho.hessian.client.HessianRuntimeException;
import com.caucho.hessian.client.HessianHttpProxyFactory.HessianHttpProxy;
import com.pccw.hessian.support.cache.CacheHandler;
import com.pccw.hessian.support.cache.CachePolicy;
import com.pccw.hessian.support.cache.OptModel;

/**
 * Hessian cache,asynchronous,http Proxy
 * @author guxuede
 *
 */
public class HessianCAHProxy extends HessianHttpProxy{
	//private static final Logger log = Logger.getLogger(HessianCAHProxy.class.getName());
	private CacheHandler cacheHandler;
	private ExceptionHandler exceptionHandler;
	
	public HessianCAHProxy(HessianProxyFactory factory, URL url,CacheHandler cacheHandler,ExceptionHandler exceptionHandler,RemoteProxy remoteURL) {
		super(factory, url);
		this.cacheHandler=cacheHandler;
		this.exceptionHandler=exceptionHandler;
		if(remoteURL!=null){
			remoteURL.setProxy(this);
		}
	}
	
	
	
	@SuppressWarnings("unchecked")
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
		/**
		 * =========================================异步任务块
		 */
		TaskExecuteListener<Object,Throwable> taskExecuteListener=null;
		if(args!=null && args.length > 0 && args[args.length-1] instanceof TaskExecuteListener){//检测该方法是否为异步方法，检测的依据是改方法的参数列表中最后一个参数类型是TaskExecuteListener
			taskExecuteListener=(TaskExecuteListener<Object,Throwable>) args[args.length-1];
			args=SupportUtils.copyOfRange(args, 0, args.length-1);
			method=getNoAsynMethod(method);//将方法和参数都换成非异步的方法和参数
			taskExecuteListener.onPerExecute(method, args);
		}
		/**
		 * =========================================缓存块(读)
		 */
		OptModel model=(method.getAnnotation(CachePolicy.class)==null || cacheHandler==null)?OptModel.NONE:method.getAnnotation(CachePolicy.class).model();
    	if(model.equals(OptModel.READ) || (model.equals(OptModel.BOTH) && cacheHandler.ifReturnFromCahce(method, args))){//如果需要从缓存中获取，则直接从缓存中取
    		Object result=cacheHandler.returnFromCahce(method, args);
    		if(taskExecuteListener!=null){
    			taskExecuteListener.onExecuteSuccess(method, args, result);
    			taskExecuteListener.onFinally(method, args,result);
    		}
    		return result;
    	}
    	
		if(taskExecuteListener!=null){//如果需要向远程获取数据且发现为异步任务，那么启动异步方法执行任务，本方法直接返回null
			invokeAsyn(proxy, method, args,model,cacheHandler,taskExecuteListener);
			return null;
		}else{//直接阻塞式向远程服务端请求数据
			try {
				Object result=super.invoke(proxy, method, args);
				if(model.equals(OptModel.WRITE) || model.equals(OptModel.BOTH)){
					cacheHandler.onExecuteSuccess(method, args,result);
				}
				return result;
			} catch (Throwable e) {
				throw exceptionHandler.parseException(e);
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	private void invokeAsyn(final Object proxy, final Method method, final Object[] args,final OptModel model,final CacheHandler cacheHandler,final TaskExecuteListener<Object,Throwable> taskExecuteListener){
		((HessianCAHProxyFactory)_factory).getAsynTaskExecuter().execute(new HessianAsynTask<Object,Throwable>() {
			@Override
			public Object doInBackground() throws Throwable {
				return HessianCAHProxy.super.invoke(proxy, method, args);
			}
			@Override
			public void onExceptionOccured(Throwable exception) {
				exception=exceptionHandler.parseException(exception);
				taskExecuteListener.onExceptionOccured(method, args, exception);
			}
			@Override
			public void onExecuteSuccess(Object result) {
				taskExecuteListener.onExecuteSuccess(method, args, result);
				if(model.equals(OptModel.WRITE) || model.equals(OptModel.BOTH)){
					cacheHandler.onExecuteSuccess(method, args, result);
				}
			}
			@Override
			public void onFinally(Object result) {
				taskExecuteListener.onFinally(method, args, result);
			}
		});
		
	}
	
	/**
	 * 得到异步方法的非异步方法。
	 * 比如：	login(String username,String password,TaskExecuteListener listener)
	 * 一定存在：login(String username,String password);
	 * 否则将抛出NoSuchMethodException
	 * @param proxy
	 * @param method
	 * @return
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 */
	private Method getNoAsynMethod(Method method){
		Class<?>[] src=method.getParameterTypes();
		try {
			return method.getDeclaringClass().getDeclaredMethod(method.getName(), SupportUtils.copyOfRange(src,0,src.length-1));
		} catch (Exception e) {
			throw new HessianRuntimeException(e);
		}
	}

	
	public static class RemoteProxy{
		private HessianCAHProxy proxy;

		public HessianCAHProxy getProxy() {
			return proxy;
		}
		public void setProxy(HessianCAHProxy proxy) {
			this.proxy = proxy;
		}
	}
}
